#include "root.h"
#include "lex.h"
#include "parse.h"
#include "exec.h"

// echo abc >log
//
// redirect
//   basic
//   >
//   log
//
// Inside child process
//
// cat <input >ouput
// redirct
//   redirect
//     basic
//       cat
//     <
//     input
//  >
//  output
void tree_execute_redirect(tree_t *this)
{
    tree_t* body = tree_get_child(this, 0);    
    tree_t* operator = tree_get_child(this, 1);
    tree_t* file = tree_get_child(this, 2);
    char* path = file->token;
    int fd;
    int redirect_fd;

    if (token_is(operator, "<"))
    {
        fd = open(path, O_RDONLY);
        redirect_fd = 0;
    }
    if (token_is(operator, ">"))
    {
        fd = open(path, O_CREAT | O_TRUNC | O_WRONLY);
        redirect_fd = 1;
    }
    if (token_is(operator, ">>"))
    {
        fd = open(path, O_CREAT | O_WRONLY | O_APPEND);
        redirect_fd = 1;
    }
    
    assert(fd >= 0);
    dup2(fd, redirect_fd);
    close(fd);
    tree_execute(body);
}

#define MAX_ARGC 16
void tree_execute_basic(tree_t *this)
{
    int argc = 0;
    char* argv[MAX_ARGC];
    

    int i;
    tree_t* child;
    vector_each(&this->child_vector, i, child)
        argv[argc++] = child->token;
    // puts("here3");

    argv[argc] = NULL;
    
    execvp(argv[0], argv);
}

// echo abc | grep b
//
// pipe
//   basic
//     echo
//     abc
//   basic
//     grep
//     b
//
// cmdA | cmdB | cmdC
// pipe
//   pipe 
//     cmdA
//     cmdB
//   cmdC
void tree_execute_pipe(tree_t *this)
{
    int fd[2];

    tree_t* first = tree_get_child(this, 0);
    tree_t* second = tree_get_child(this, 1);

    pipe(fd);
    pid_t pid = fork();
    if (pid == 0)
    {
        dup2(fd[1], 1);
        close(fd[0]);
        close(fd[1]);
        tree_execute(first);
    }
    else
    {
        dup2(fd[0], 0);
        close(fd[0]);
        close(fd[1]);
        tree_execute(second);
    }
}

// # line 
// "cd /bin"
//
// # tree
// TREE_BASIC
//   cd
//   /bin
int tree_execute_builtin(tree_t *this)
{
    if (this->type == TREE_BASIC)
    {
        int argc = this->child_vector.count;
        tree_t* child = tree_get_child(this, 0);
        char* argv = child->token;

        if (strcmp(argv, "exit") == 0)
        {
            exit(0);
            return 1;
        }
        else if (strcmp(argv, "cd") == 0)
        {
            if (argc != 1)
            {
                tree_t* child = tree_get_child(this, 1);
                int error = chdir(child->token);
                if (error < 0)
                    perror("cd");

                return 1;
            }
        }
        else if (strcmp(argv, "pwd") == 0)
        {
            char path[512];
            getcwd(path, sizeof(path));
            puts(path);
            return 1;
        }
    }

    return 0;
}

// cc a-very-large-file.c &
// async
//   basic
//     cc
//     a-very-large-file
//
// cat file | grep string &
// async
//   pipe
//     basic
//       cat
//       file
//     basic
//       grep
//       string
//
// child count == 1
void tree_execute_async(tree_t *this)
{
    tree_t* body = tree_get_child(this, 0);
    tree_execute(body);
}

// Inside child process
void tree_execute(tree_t *this)
{
    switch (this->type) {
        case TREE_ASYNC:
            tree_execute_async(this); 
            break;

        case TREE_PIPE:
            tree_execute_pipe(this); 
            break;

        case TREE_REDIRICT:
            tree_execute_redirect(this); 
            break;

        case TREE_BASIC:
            tree_execute_basic(this); 
            break;
    }
}

// Inside parent(shell) process
void tree_execute_wrapper(tree_t *this)
{
    if (tree_execute_builtin(this))
    {
        // puts("tree_execute_builtin");  //debug
        return;
    }

    int status;
    pid_t pid = fork();
    if (pid == 0) {
        // puts("in child process");  //debug

        tree_execute(this);
        exit(EXIT_FAILURE);
    }
   
    // cc a-large-file.c &
    if (this->type != TREE_ASYNC)
        wait(&status);
}

// cmd1 <input | cmd2 | cmd3 >output &
